package dev.ece.core.cells.model.action;

import java.util.Map;

public abstract class ActionManager<T extends Number & Comparable<T>> {

	private Action<T> action;
	
	private ActionListener<T> actionListener;
	
	/**
	 * 运动过程
	 */
	private int active;
	
	/**
	 * 当前值
	 */
	private T current = zero();
	
	/**
	 * 使用动作
	 * @param name
	 */
	public final void action(String name, Map<String, Action<T>> actions) {
		if(!actions.containsKey(name)) {
			return;
		}
		this.action = actions.get(name);
		this.restart();
		//动作开始监听
		if(this.actionListener != null) {
			this.actionListener.onActionStart(this);
		}
	}
	
	/**
	 * 重新动作
	 */
	public final void restart() {
		if(this.action != null) {
			this.current = this.action.start;
		} else {
			this.current = zero() ;
		}
		this.active = ActionConstants.ACTIVE_PREPARE;
	}
	
	public abstract T zero();
	
	public abstract T diff(T current, T end);
	
	public abstract T currentAddDiff(T current, T diff);
	
	public abstract T increment(T current);
	
	/**
	 * 停止动作
	 */
	public final void stop() {
		this.active = ActionConstants.ACTIVE_FINISH;
	}
	
	/**
	 * 获取动画当前值
	 * @return
	 */
	public final T current() {
		if(this.active == ActionConstants.ACTIVE_FINISH) {
			this.active = ActionConstants.ACTIVE_HOLD;
			//动作结束监听
			if(this.actionListener != null) {
				this.actionListener.onActionFinish(this);
			}
			return this.current;
		}
		//保持当前
		if(this.active == ActionConstants.ACTIVE_HOLD) {
			return this.current;
		}
		if(this.action != null) {
			if(this.active == ActionConstants.ACTIVE_ACTION) {
				this.current = increment(current);
				//>=
				if(this.current.compareTo(this.action.getEnd()) > -1) {
					//动作结束
					T diff = diff(this.current, this.action.getEnd());
					this.current = this.action.getEnd();
					//循环
					if(this.action.isLoop()) {
						if(diff.compareTo(zero()) > 0) {
							this.restart();
							this.active = ActionConstants.ACTIVE_ACTION;
							this.current = currentAddDiff(this.current, diff);
							return this.current;
						} else {
							try {
								return this.current;
							} finally {
								this.restart();
							}
						}
					} else {
						this.stop();
					}
					//动作结束监听
					if(this.actionListener != null) {
						this.actionListener.onActionFinish(this);
					}
				}
			} else if(this.active == ActionConstants.ACTIVE_PREPARE){
				this.active = ActionConstants.ACTIVE_ACTION;
			}
		}
		return this.current;
	}
	
	public final Action<T> getAction() {
		return action;
	}
	
	public final ActionListener<T> getActionListener() {
		return actionListener;
	}
	
	public final void setActionListener(ActionListener<T> actionListener) {
		this.actionListener = actionListener;
	}

	/**
	 * 获取动作状态
	 * @return
	 */
	public final int getActive() {
		return active;
	}
	
	/**
	 * 向动作组中添加动作
	 * @param actions
	 * @param action
	 */
	public final static <T extends Number & Comparable<T>> void addActions(Map<String, Action<T>> actions, Action<T>[] action) {
		Action<T> t_action = null;
		for(int i=0; i<action.length; i++) {
			t_action = action[i];
			actions.put(t_action.getName(), t_action);
		}
	}
	
	/**
	 * 向动作组中添加动作
	 * @param actions
	 * @param action
	 */
	public final static <T extends Number & Comparable<T>> void addAction(Map<String, Action<T>> actions, Action<T> action) {
		actions.put(action.getName(), action);
	}
	
	public static interface ActionListener<T extends Number & Comparable<T>> {
		
		/**
		 * 动作开始
		 * @param actionManager
		 */
		public void onActionStart(ActionManager<T> actionManager);
		
		/**
		 * 动作结束
		 */
		public void onActionFinish(ActionManager<T> actionManager);
		
	}
	
	public static class Action<T extends Number & Comparable<T>> {

		private String name;
		
		private T start;
		
		private T end;
		
		private boolean loop;
		
		public Action(String name, T start, T end) {
			this(name, start, end, false);
		}
		
		public Action(String name, T start, T end, boolean loop) {
			this.name = name;
			this.start = start;
			this.end = end;
			this.loop = loop;
		}
		
		public String getName() {
			return name;
		}

		public T getStart() {
			return start;
		}

		public T getEnd() {
			return end;
		}

		public boolean isLoop() {
			return loop;
		}
		
	}
}
